#ifndef __MY_UTIL__
#define __MY_UTIL__
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <experimental/filesystem>
#include <memory>
#include <jsoncpp/json/json.h>
#include "bundle.h"

namespace cloud
{
    namespace fs = std::experimental::filesystem; // C++17支持
    class FileUtil
    {
    public:
        FileUtil(const std::string &filename)
            : _filename(filename)
        {
        }

        bool Remove()   //删除源文件
        {
            if (this->Exists() == false)
            {
                return true;
            }
            remove(_filename.c_str());
            return true;
        }

        int64_t FileSize() // 获取文件大小
        {
            struct stat st; // 文件信息结构体
            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "get file size failed!\n";
                return -1;
            }
            return st.st_size;
        }

        time_t LastMTime() // 获取最后一次修改时间
        {
            struct stat st;
            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "get file LastMTime failed!\n";
                return -1;
            }
            return st.st_mtime;
        }

        time_t LastATime() // 获取最后一次访问时间
        {
            struct stat st;
            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "get file LastAtime failed!\n";
                return -1;
            }
            return st.st_atime;
        }

        std::string FileName() // 获取文件名称
        {
            // 形式 ./a/test.txt 获取文件名只需找最后一个‘/’即可
            size_t pos = _filename.find_last_of("/");
            if (pos == std::string::npos)
            {
                return _filename; // 本身就是文件名
            }
            return _filename.substr(pos + 1);
        }

        bool GetPosLen(std::string *body, size_t pos, size_t len) // 从文件中指定位置读取指定长度的内容
        {
            // 获取文件大小
            size_t fsize = this->FileSize();

            // 检查请求的位置和长度是否超出文件范围
            if (pos + len > fsize)
            {
                std::cout << "get file len is error\n";
                return false; // 超出范围则返回错误
            }

            // 创建文件输入流对象
            std::ifstream ifs;
            // 以二进制模式打开文件
            ifs.open(_filename, std::ios::binary);

            // 检查文件是否成功打开
            if (ifs.is_open() == false)
            {
                std::cout << "read open file failed!\n";
                return false; // 打开失败返回错误
            }

            // 定位到指定位置
            ifs.seekg(pos, std::ios::beg);

            // 调整字符串大小以容纳要读取的数据
            body->resize(len);

            // 从文件读取指定长度的数据到字符串中
            ifs.read(&(*body)[0], len);

            // 检查读取操作是否成功
            if (ifs.good() == false)
            {
                std::cout << "read file Content failed!\n";
                ifs.close();  // 关闭文件
                return false; // 读取失败返回错误
            }

            // 关闭文件
            ifs.close();
            return true; // 操作成功返回true
        }

        bool GetContent(std::string *body) // 获取整个文件内容的函数
        {
            // 获取文件大小
            size_t fsize = this->FileSize();
            return GetPosLen(body, 0, fsize);
        }

        bool SetContent(const std::string &body) // 设置文件内容的函数
        {
            std::ofstream ofs;
            ofs.open(_filename, std::ios::binary);
            // 检查文件是否成功打开
            if (ofs.is_open() == false)
            {
                std::cout << "write open file failed!\n";
                return false; // 打开失败返回错误
            }
            ofs.write(&body[0], body.size()); // 写入
            // 检查写入操作是否成功
            if (ofs.good() == false)
            {
                std::cout << "write file Content failed!\n";
                ofs.close();  // 关闭文件
                return false; // 写入失败返回错误
            }
            // 关闭文件
            ofs.close();
            return true; // 操作成功返回true
        }

        bool Compress(const std::string &packname) // 压缩
        {
            // 1.获取源文件数据
            std::string body;
            if (this->GetContent(&body) == false)
            {
                std::cout << "compress get file content failed!\n";
                return false;
            }
            // 2.对数据进行压缩
            std::string packed = bundle::pack(bundle::LZIP, body);
            // 3.将压缩的数据存储到压缩包文件中
            FileUtil fu(packname);
            if (fu.SetContent(packed) == false)
            {
                std::cout << "compress write packed data failed!\n";
                return false;
            }
            return true;
        }

        bool UnCompress(const std::string &filename) // 压缩
        {
            // 将当前压缩包数据读取出来
            std::string body;
            if (this->GetContent(&body) == false)
            {
                std::cout << "uncompress get file content failed!\n";
                return false;
            }
            // 对压缩的数据进行解压缩
            std::string unpacked = bundle::unpack(body);
            // 将解压缩的数据写入到新文件
            FileUtil fu(filename);
            if (fu.SetContent(unpacked) == false)
            {
                std::cout << "uncompress write packed data failed!\n";
                return false;
            }
            return true;
        }

        bool Exists() // 文件是否存在
        {
            return fs::exists(_filename); // C++17支持
        }

        bool CreateDirectory() // 创建目录
        {
            if (this->Exists())
                return true;                          // 如果目录已存在，直接返回 true
            return fs::create_directories(_filename); // C++17支持 递归创建多级目录
        }

        bool ScanDirectory(std::vector<std::string> *arry) // 浏览目录
        {
            // 扫描指定目录下的所有文件（不包括子目录），并将它们的 相对路径 存入 arry。
            for (auto &p : fs::directory_iterator(_filename))
            {
                if (fs::is_directory(p) == true)
                {
                    continue;
                }
                // relarive_path 带有路径的文件名
                arry->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }

    private:
        std::string _filename;
    };

    class JsonUtil
    {
    public:
        // 将 Json::Value 对象序列化为 JSON 字符串
        // 参数：
        //   root - 要序列化的 Json::Value 对象
        //   str - 输出参数，用于存储生成的 JSON 字符串
        // 返回值：
        //   成功返回 true，失败返回 false
        static bool Serialize(const Json::Value &root, std::string *str)
        {
            // 创建 StreamWriterBuilder 用于配置 JSON 写入选项
            Json::StreamWriterBuilder swb;

            // 创建 StreamWriter 对象用于实际写入操作
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());

            // 使用字符串流作为输出目标
            std::stringstream ss;

            // 将 JSON 数据写入字符串流
            if (sw->write(root, &ss) != 0)
            {
                // 写入失败时输出错误信息
                std::cout << "json write failed!\n";
                return false;
            }

            // 将字符串流内容赋给输出参数
            *str = ss.str();
            return true;
        }

        // 将 JSON 字符串反序列化为 Json::Value 对象
        // 参数：
        //   str - 要解析的 JSON 字符串
        //   root - 输出参数，用于存储解析后的 Json::Value 对象
        // 返回值：
        //   成功返回 true，失败返回 false
        static bool UnSerialize(const std::string &str, Json::Value *root)
        {
            // 创建 CharReaderBuilder 用于配置 JSON 解析选项
            Json::CharReaderBuilder crb;

            // 创建 CharReader 对象用于实际解析操作
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());

            // 用于存储解析错误信息
            std::string err;

            // 解析 JSON 字符串
            bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), root, &err);
            if (ret == false)
            {
                // 解析失败时输出错误信息
                std::cerr << "parse error:" << err << std::endl;
                return false;
            }

            return true;
        }
    };
}

#endif